﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using DataTier.Entities;
using DataTier.Helpers;

namespace DataTier.Repository
{
    /// <summary>
    /// Репозиторий для работы с продуктами
    /// </summary>
    public class ProductsRepository : RepositoryBase
    {
        public ProductsRepository()
        {

        }

        public ProductsRepository(IDbConnection connection)
            : base(connection)
        {

        }

        /// <summary>
        /// Возвращает объект Товар
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public ProductEntity GetProduct(int currentUserId, int id)
        {
            using (var dbcontext = CreateDataContext())
            {
                var product = dbcontext.Product.Include("ProductGroup").Include("MeasureUnit").Where(p => p.ProductId == id).FirstOrDefault();
                return EntityHelper.GetProductEntity(product);
            }
        }


        /// <summary>
        /// Возвращает список Товаров
        /// </summary>
        /// <returns></returns>
        public List<ProductEntity> GetProductList(int currentUserId, int numPage = 1, int onPage = 10, SearchParameters param = null, string sortColumn = null, string sortDirection = null)
        {
            using (var dbcontext = CreateDataContext())
            {
                IQueryable<Product> products = GetProductListQueryable(dbcontext, currentUserId, numPage, onPage, param, sortColumn, sortDirection);
                List<Product> productList = products.ToList();
                return productList.Select(p => EntityHelper.GetProductEntity(p)).ToList();
            }
        }

        private IQueryable<Product> GetProductListQueryable(DBModelContainer dbcontext, int currentUserId, int numPage = 1, int onPage = 10, SearchParameters param = null, string sortColumn = null, string sortDirection = null)
        {
            //var products = dbcontext.Product.Include("ProductGroup").Where(p => p.Deleted != true).OrderBy(p => p.ProductName).ToList();
            //return products;

            IQueryable<Product> products = dbcontext.Product
                .Include("ProductGroup")
                .Include("MeasureUnit")
                .Where(p => p.Deleted != true);

            #region Применение фильтров
            if (param != null)
            {
                if (!string.IsNullOrEmpty(param.ProductArtikul))
                    products = products
                        .Where(p => p.Artikul.Substring(0, param.ProductArtikul.Length).ToLower() == param.ProductArtikul.ToLower()); // Начинается со строки

                if (!string.IsNullOrEmpty(param.ProductName))
                    products = products
                        .Where(p => p.ProductName.ToLower().Contains(param.ProductName.ToLower())); // Вхождение строки
                //.Where(p => p.ProductName.Substring(0, param.ProductName.Length).ToLower() == param.ProductName.ToLower()); // Начинается со строки

                if (!string.IsNullOrEmpty(param.ProductSupplierArtikul))
                    products = products
                        .Where(p => p.SupplierArtikul.Substring(0, param.ProductSupplierArtikul.Length).ToLower() == param.ProductSupplierArtikul.ToLower()); // Начинается со строки

                if (!string.IsNullOrEmpty(param.ProductSupplierName))
                    products = products
                        .Where(p => p.SupplierName.ToLower() == param.ProductSupplierName.ToLower()); // Полное соответствие строки
                //.Where(p => p.SupplierName.Substring(0, param.ProductSupplierName.Length).ToLower() == param.ProductSupplierName.ToLower()); // Начинается со строки

                if (param.ProductId.HasValue)
                    products = products
                        .Where(p => p.ProductId == param.ProductId);

                if (string.IsNullOrEmpty(param.ProductVisibleState))
                    param.ProductVisibleState = rbVisibleState.visibled;
                switch (param.ProductVisibleState)
                {
                    case rbVisibleState.visibled:
                        products = products.Where(p => p.IsHidden == false);
                        break;
                    case rbVisibleState.hidden:
                        products = products.Where(p => p.IsHidden == true);
                        break;
                    case rbVisibleState.archived:
                        products = products.Where(p => p.IsArchive == true);
                        break;
                    default:
                        break;
                }
            }
            #endregion Применение фильтров

            //sortColumn, sortDirection

            #region Применение сортировки

            if (string.IsNullOrEmpty(sortColumn))
                sortColumn = "ProductId";

            bool isAscending = false;
            if (!string.IsNullOrEmpty(sortDirection) && sortDirection.ToLower() == "asc")
                isAscending = true;

            var list = products;
            if (sortColumn == "Artikul")
            {
                if (isAscending)
                    list = products.OrderBy(p => p.Artikul);
                else
                    list = products.OrderByDescending(p => p.Artikul);
            }
            else if (sortColumn == "Price")
            {
                if (isAscending)
                    list = products.OrderBy(p => p.Price);
                else
                    list = products.OrderByDescending(p => p.Price);
            }
            else if (sortColumn == "ProductId")
            {
                if (isAscending)
                    list = products.OrderBy(p => p.ProductId);
                else
                    list = products.OrderByDescending(p => p.ProductId);
            }
            else if (sortColumn == "ProductName")
            {
                if (isAscending)
                    list = products.OrderBy(p => p.ProductName);
                else
                    list = products.OrderByDescending(p => p.ProductName);
            }
            else if (sortColumn == "SupplierArtikul")
            {
                if (isAscending)
                    list = products.OrderBy(p => p.SupplierArtikul);
                else
                    list = products.OrderByDescending(p => p.SupplierArtikul);
            }
            else if (sortColumn == "SupplierName")
            {
                if (isAscending)
                    list = products.OrderBy(p => p.SupplierName);
                else
                    list = products.OrderByDescending(p => p.SupplierName);
            }

            #endregion Применение сортировки

            //List<Product> productList = products.OrderByDescending(p => p.ProductId).Skip(onPage * (numPage - 1)).Take(onPage).ToList();
            IQueryable<Product> productList = list.Skip(onPage * (numPage - 1)).Take(onPage);

            return productList;
        }


        /// <summary>
        /// Возвращает количество Продуктов из базы данных
        /// </summary>
        /// <returns></returns>
        public int GetProductListCount(int currentUserId, SearchParameters param = null)
        {
            using (var dbcontext = CreateDataContext())
            {
                IQueryable<Product> list = GetProductListQueryable(dbcontext, currentUserId, 1, 100000000, param);
                return list.Count();
            }
        }


        /// <summary>
        /// Возвращает список Товаров без тех, у которых установлен признак СКРЫТ
        /// </summary>
        /// <returns></returns>
        public List<ProductEntity> GetNotHiddenProductList(int currentUserId)
        {
            using (var dbcontext = CreateDataContext())
            {
                var products = dbcontext.Product.Include("ProductGroup").Include("MeasureUnit").Where(p => p.Deleted != true && p.IsHidden == false).OrderBy(p => p.ProductName).ToList();
                return products.Select(p => EntityHelper.GetProductEntity(p)).ToList();
            }
        }


        /// <summary>
        /// Возвращает список Категорий Товаров
        /// </summary>
        /// <returns></returns>
        public List<ProductGroup> GetProductGroupList(int currentUserId)
        {
            using (var dbcontext = CreateDataContext())
            {
                var groups = dbcontext.ProductGroup.ToList();
                return groups;
            }
        }


        /// <summary>
        /// Добавляет новый продукт или обновляется уже существующий
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="product"></param>
        /// <returns></returns>
        public int SaveProduct(int currentUserId, ProductEntity product)
        {
            using (var dbcontext = CreateDataContext())
            {
                int productId = 0;
                product.Product.MeasureUnitId = product.MeasureUnit.Id;
                product.Product.IsUnavailable = (product.Product.Price != null) ? false : true; // Если цена непустая, то ТоварНеВСписке=false, иначе - true
                if (product.Product.ProductId > 0) // Если происходит обновление уже существующей записи о Продукте в БД
                {
                    var updatedProduct = dbcontext.Product.Where(p => p.ProductId == product.Product.ProductId).FirstOrDefault();
                    if (updatedProduct != null)
                    {
                        // Обновление заказов, в которых присутствует данный товар и у которых не существует выписанных накладных или накладные есть, но без данного товара
                        var orders = dbcontext.Order.Include("OrderSpec")
                            .Where(o => o.OrderSpec.Where(s => s.ProductId == product.Product.ProductId).Any() &&
                                (!o.ExpendBill.Any() || !o.ExpendBill.Where(b => b.ExpendBillSpec.Where(ebs => ebs.ProductId == product.Product.ProductId).Any()).Any())
                            ).ToList();

                        if (orders != null)
                            foreach (var o in orders)
                            {
                                foreach (var spec in o.OrderSpec)
                                {
                                    if (spec.ProductId == product.Product.ProductId)
                                    {
                                        spec.Artikul = product.Product.Artikul;
                                        spec.IsUnavailable = product.Product.IsUnavailable;
                                        spec.Price = product.Product.Price;
                                        spec.ProductName = product.Product.ProductName;
                                        spec.SupplierArtikul = product.Product.SupplierArtikul;
                                        spec.SupplierName = product.Product.SupplierName;
                                        spec.Summa = ((spec.Price.HasValue) ? spec.Price.Value : 0.0) * (double)spec.Count;
                                    }
                                }

                                // Обновляется итоговая сумма заказа
                                o.ItogoSumm = o.OrderSpec.Sum(s => s.Summa);
                            }


                        // Обновление заказов, в которых присутствует данный товар и у которых есть выписанные накладные на ЧАСТЬ данного товара
                        var ordersExistEB = dbcontext.Order.Include("OrderSpec")
                            .Where(o => o.OrderSpec.Where(s => s.ProductId == product.Product.ProductId).Any() &&
                                o.ExpendBill.Where(b => b.ExpendBillSpec.Where(ebs => ebs.ProductId == product.Product.ProductId).Any()).Any()
                            ).ToList();

                        if (ordersExistEB != null)
                        {
                            foreach (var o in ordersExistEB)
                            {
                                var orderSpec = o.OrderSpec.Where(os => os.ProductId == product.Product.ProductId).FirstOrDefault();
                                int countProductinExpendBill = 0;
                                foreach (var bill in o.ExpendBill)
                                    countProductinExpendBill += bill.ExpendBillSpec.Where(ebs => ebs.ProductId == product.Product.ProductId).Select(ebs => ebs.Count).Sum();

                                // Если количество товара в Заказе больше общего количества товара в выписанных накладных, то уменьшаем количество товара в строке Заказа и добавляем новую строку с остатками количества и новой ценой товара
                                if (orderSpec.Count > countProductinExpendBill)
                                {
                                    // Создается новый товар и добавляется в БД
                                    var newProduct = new Product()
                                    {
                                        Artikul = (!string.IsNullOrEmpty(product.Product.Artikul)) ? product.Product.Artikul : updatedProduct.Artikul,
                                        GroupId = (product.Product.GroupId != null) ? product.Product.GroupId : updatedProduct.GroupId,
                                        ImageBigUrl = (!string.IsNullOrEmpty(product.Product.ImageBigUrl)) ? product.Product.ImageBigUrl : updatedProduct.ImageBigUrl,
                                        ImageSmallUrl = (!string.IsNullOrEmpty(product.Product.ImageSmallUrl)) ? product.Product.ImageSmallUrl : updatedProduct.ImageSmallUrl,
                                        IsUnavailable = (product.Product.IsUnavailable != null) ? product.Product.IsUnavailable : updatedProduct.IsUnavailable,
                                        Price = (product.Product.Price != null) ? product.Product.Price : updatedProduct.Price,
                                        ProductName = (!string.IsNullOrEmpty(product.Product.ProductName)) ? product.Product.ProductName : updatedProduct.ProductName,
                                        SupplierArtikul = (!string.IsNullOrEmpty(product.Product.SupplierArtikul)) ? product.Product.SupplierArtikul : updatedProduct.SupplierArtikul,
                                        SupplierName = (!string.IsNullOrEmpty(product.Product.SupplierName)) ? product.Product.SupplierName : updatedProduct.SupplierName,
                                        IsHidden = product.Product.IsHidden,
                                        MeasureUnitId = product.Product.MeasureUnitId
                                    };
                                    dbcontext.Product.AddObject(newProduct);
                                    updatedProduct.IsHidden = true; //Скрываем старую версию товара для заказчика
                                    updatedProduct.IsArchive = true; //Старая версия товара помечается как Архивная

                                    var oSpec = new OrderSpec()
                                    {
                                        Product = newProduct,
                                        RowId = o.OrderSpec.Select(ords => ords.RowId).Max() + 1,
                                        Artikul = newProduct.Artikul,
                                        IsUnavailable = newProduct.IsUnavailable,
                                        Price = newProduct.Price,
                                        ProductName = newProduct.ProductName,
                                        SupplierArtikul = newProduct.SupplierArtikul,
                                        SupplierName = newProduct.SupplierName,
                                        Count = orderSpec.Count - countProductinExpendBill,
                                        Summa = ((newProduct.Price.HasValue) ? newProduct.Price.Value : 0.0) * (double)(orderSpec.Count - countProductinExpendBill)
                                    };
                                    o.OrderSpec.Add(oSpec);

                                    orderSpec.Count = countProductinExpendBill;
                                    orderSpec.Summa = ((orderSpec.Price.HasValue) ? orderSpec.Price.Value : 0.0) * (double)orderSpec.Count;
                                }

                                // Обновляется итоговая сумма заказа
                                o.ItogoSumm = o.OrderSpec.Sum(s => s.Summa);
                            }
                        }

                        updatedProduct.Artikul = product.Product.Artikul;
                        updatedProduct.GroupId = (product.Product.GroupId != null) ? product.Product.GroupId : updatedProduct.GroupId;
                        updatedProduct.ImageBigUrl = (!string.IsNullOrEmpty(product.Product.ImageBigUrl)) ? product.Product.ImageBigUrl : updatedProduct.ImageBigUrl;
                        updatedProduct.ImageSmallUrl = (!string.IsNullOrEmpty(product.Product.ImageSmallUrl)) ? product.Product.ImageSmallUrl : updatedProduct.ImageSmallUrl;
                        updatedProduct.IsUnavailable = (product.Product.IsUnavailable != null) ? product.Product.IsUnavailable : updatedProduct.IsUnavailable;
                        updatedProduct.Price = (product.Product.Price != null) ? product.Product.Price : updatedProduct.Price;
                        updatedProduct.ProductName = (!string.IsNullOrEmpty(product.Product.ProductName)) ? product.Product.ProductName : updatedProduct.ProductName;
                        updatedProduct.SupplierArtikul = product.Product.SupplierArtikul; //updatedProduct.SupplierArtikul = (!string.IsNullOrEmpty(product.SupplierArtikul)) ? product.SupplierArtikul : updatedProduct.SupplierArtikul;
                        updatedProduct.SupplierName = product.Product.SupplierName; //updatedProduct.SupplierName = (!string.IsNullOrEmpty(product.SupplierName)) ? product.SupplierName : updatedProduct.SupplierName;
                        updatedProduct.IsHidden = product.Product.IsHidden;
                        updatedProduct.MeasureUnitId = product.Product.MeasureUnitId;
                    }
                    else
                    {
                        dbcontext.Product.Attach(product.Product);
                        dbcontext.ObjectStateManager.ChangeObjectState(product, EntityState.Modified);
                    }
                }
                else // Если добавляется новый Продукт
                {
                    dbcontext.AddToProduct(product.Product);
                }

                dbcontext.SaveChanges();

                productId = product.Product.ProductId;

                return productId;
            }
        }


        /// <summary>
        /// Обновляет картинку с изображением продукта
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="productId"></param>
        /// <param name="ImageBigUrl"></param>
        /// <param name="ImageSmallUrl"></param>
        public void UpdateImageProduct(int currentUserId, int productId, string ImageBigUrl, string ImageSmallUrl)
        {
            if (productId == 0)
                return;

            using (var dbcontext = CreateDataContext())
            {
                var product = dbcontext.Product.Where(p => p.ProductId == productId).FirstOrDefault();

                if (product == null)
                    return;

                product.ImageBigUrl = ImageBigUrl;
                product.ImageSmallUrl = ImageSmallUrl;

                dbcontext.SaveChanges();
            }
        }


        /// <summary>
        /// Удаление Продукта из базы данных
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="productId"></param>
        public void DeleteProduct(int currentUserId, int productId)
        {
            #region //Вариант через физическое удаление записи из БД
            //using (var dbcontext = CreateDataContext())
            //{
            //    if (productId == 0)
            //        return;

            //    var product = dbcontext.Product.Where(p => p.ProductId == productId).FirstOrDefault();
            //    if (product != null)
            //        dbcontext.Product.DeleteObject(product);

            //    dbcontext.SaveChanges();
            //}
            #endregion //Вариант через физическое удаление записи из БД

            #region Вариант через установки маркера "удален" записи в БД
            using (var dbcontext = CreateDataContext())
            {
                if (productId == 0)
                    return;

                var product = dbcontext.Product.Where(p => p.ProductId == productId).FirstOrDefault();
                if (product != null)
                    product.Deleted = true;

                dbcontext.SaveChanges();
            }
            #endregion Вариант через установки маркера "удален" записи в БД
        }


        /// <summary>
        /// Скрытие продукта (установка признака СКРЫТ)
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="productId"></param>
        /// <param name="isHidden"></param>
        public void SetIsHidden(int currentUserId, int productId, bool isHidden)
        {
            using (var dbcontext = CreateDataContext())
            {
                if (productId == 0)
                    return;

                var product = dbcontext.Product.Where(p => p.ProductId == productId).FirstOrDefault();
                if (product != null)
                    product.IsHidden = isHidden;

                dbcontext.SaveChanges();
            }
        }


        /// <summary>
        /// Возвращает список единиц измерения Товаров
        /// </summary>
        /// <returns></returns>
        public List<MeasureUnit> GetMeasureUnitList(int currentUserId)
        {
            using (var dbcontext = CreateDataContext())
            {
                var units = dbcontext.MeasureUnit.ToList();
                return units;
            }
        }


        /// <summary>
        /// Возвращает единицу измерения Товара по ID
        /// </summary>
        /// <returns></returns>
        public MeasureUnit GetMeasureUnitById(int currentUserId, int measureUnitId)
        {
            using (var dbcontext = CreateDataContext())
            {
                var unit = dbcontext.MeasureUnit.Where(m => m.Id == measureUnitId).FirstOrDefault();
                return unit;
            }
        }


    }
}
